// openGLExt.cpp

#include "openGLExt.h"

#define GET_FUN_ADDR(fn_t, entry, name)  entry.name = (fn_t)wglGetProcAddress(#name);   \
                                            if (NULL == entry.name) break

typedef struct gl_1_2_entry
{
    PFNGLBLENDCOLORPROC glBlendColor;
    PFNGLBLENDEQUATIONPROC glBlendEquation;
    PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements;
    PFNGLTEXIMAGE3DPROC glTexImage3D;
    PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D;
    PFNGLCOPYTEXSUBIMAGE3DPROC glCopyTexSubImage3D;
} gl_1_2_entry;

typedef struct gl_1_5_entry 
{
    PFNGLGENQUERIESPROC glGenQueries;
    PFNGLDELETEQUERIESPROC glDeleteQueries;
    PFNGLISQUERYPROC glIsQuery;
    PFNGLBEGINQUERYPROC glBeginQuery;
    PFNGLENDQUERYPROC glEndQuery;
    PFNGLGETQUERYIVPROC glGetQueryiv;
    PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv;
    PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv;
    PFNGLBINDBUFFERPROC glBindBuffer;
    PFNGLDELETEBUFFERSPROC glDeleteBuffers;
    PFNGLGENBUFFERSPROC glGenBuffers;
    PFNGLISBUFFERPROC glIsBuffer;
    PFNGLBUFFERDATAPROC glBufferData;
    PFNGLBUFFERSUBDATAPROC glBufferSubData;
    PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData;
    PFNGLMAPBUFFERPROC glMapBuffer;
    PFNGLUNMAPBUFFERPROC glUnmapBuffer;
    PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv;
    PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv;
} gl_1_5_entry;

typedef struct gl_vertex_array_object_entry
{
    PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
    PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
    PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
    PFNGLISVERTEXARRAYPROC glIsVertexArray;
} gl_vertex_array_object_entry;


gl_1_2_entry glEntry12;
gl_1_5_entry glEntry15;
gl_vertex_array_object_entry glvaoEntry;



//
// opengl 1.2 ext
//
GLboolean glextInitOpenglEntry12()
{
    static GLboolean ret = GL_FALSE;

    if (ret)
        return ret;

    do 
    {
        GET_FUN_ADDR(PFNGLBLENDCOLORPROC, glEntry12, glBlendColor);
        GET_FUN_ADDR(PFNGLBLENDEQUATIONPROC, glEntry12, glBlendEquation);
        GET_FUN_ADDR(PFNGLDRAWRANGEELEMENTSPROC, glEntry12, glDrawRangeElements);
        GET_FUN_ADDR(PFNGLTEXIMAGE3DPROC, glEntry12, glTexImage3D);
        GET_FUN_ADDR(PFNGLTEXSUBIMAGE3DPROC, glEntry12, glTexSubImage3D);
        GET_FUN_ADDR(PFNGLCOPYTEXSUBIMAGE3DPROC, glEntry12, glCopyTexSubImage3D);

        ret = GL_TRUE;
    } while (GL_FALSE);

    return ret;
}

void APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
    if (glEntry12.glBlendColor)
        glEntry12.glBlendColor(red, green, blue, alpha);
}

void APIENTRY glBlendEquation (GLenum mode)
{
    if (glEntry12.glBlendEquation)
        glEntry12.glBlendEquation(mode);
}

void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)
{
    if (glEntry12.glDrawRangeElements)
        glEntry12.glDrawRangeElements(mode, start, end, count, type, indices);
}

void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
{
    if (glEntry12.glTexImage3D)
        glEntry12.glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
}

void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels)
{
    if (glEntry12.glTexSubImage3D)
        glEntry12.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}

void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
{
    if (glEntry12.glCopyTexSubImage3D)
        glEntry12.glCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
}


//
// opengl 1.5 ext
//
GLboolean glextInitOpenglEntry15()
{
    static GLboolean ret = GL_FALSE;

    if (ret)
        return ret;

    do 
    {
        GET_FUN_ADDR(PFNGLGENQUERIESPROC, glEntry15, glGenQueries);
        GET_FUN_ADDR(PFNGLDELETEQUERIESPROC, glEntry15, glDeleteQueries);
        GET_FUN_ADDR(PFNGLISQUERYPROC, glEntry15, glIsQuery);
        GET_FUN_ADDR(PFNGLBEGINQUERYPROC, glEntry15, glBeginQuery);
        GET_FUN_ADDR(PFNGLENDQUERYPROC, glEntry15, glEndQuery);
        GET_FUN_ADDR(PFNGLGETQUERYIVPROC, glEntry15, glGetQueryiv);
        GET_FUN_ADDR(PFNGLGETQUERYOBJECTIVPROC, glEntry15, glGetQueryObjectiv);
        GET_FUN_ADDR(PFNGLGETQUERYOBJECTUIVPROC, glEntry15, glGetQueryObjectuiv);
        GET_FUN_ADDR(PFNGLBINDBUFFERPROC, glEntry15, glBindBuffer);
        GET_FUN_ADDR(PFNGLDELETEBUFFERSPROC, glEntry15, glDeleteBuffers);
        GET_FUN_ADDR(PFNGLGENBUFFERSPROC, glEntry15, glGenBuffers);
        GET_FUN_ADDR(PFNGLISBUFFERPROC, glEntry15, glIsBuffer);
        GET_FUN_ADDR(PFNGLBUFFERDATAPROC, glEntry15, glBufferData);
        GET_FUN_ADDR(PFNGLBUFFERSUBDATAPROC, glEntry15, glBufferSubData);
        GET_FUN_ADDR(PFNGLGETBUFFERSUBDATAPROC, glEntry15, glGetBufferSubData);
        GET_FUN_ADDR(PFNGLMAPBUFFERPROC, glEntry15, glMapBuffer);
        GET_FUN_ADDR(PFNGLUNMAPBUFFERPROC, glEntry15, glUnmapBuffer);
        GET_FUN_ADDR(PFNGLGETBUFFERPARAMETERIVPROC, glEntry15, glGetBufferParameteriv);
        GET_FUN_ADDR(PFNGLGETBUFFERPOINTERVPROC, glEntry15, glGetBufferPointerv);

        ret = GL_FALSE;
    } while (GL_FALSE);

    return ret;
}

void APIENTRY glGenQueries (GLsizei n, GLuint *ids)
{
    if (glEntry15.glGenQueries)
        glEntry15.glGenQueries(n, ids);
}

void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids)
{
    if (glEntry15.glDeleteQueries)
        glEntry15.glDeleteQueries(n, ids);
}

GLboolean APIENTRY glIsQuery (GLuint id)
{
    if (glEntry15.glIsQuery)
        return glEntry15.glIsQuery(id);
    return 0;
}

void APIENTRY glBeginQuery (GLenum target, GLuint id)
{
    if (glEntry15.glBeginQuery)
        glEntry15.glBeginQuery(target, id);
}

void APIENTRY glEndQuery (GLenum target)
{
    if (glEntry15.glEndQuery)
        glEntry15.glEndQuery(target);
}

void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params)
{
    if (glEntry15.glGetQueryiv)
        glEntry15.glGetQueryiv(target, pname, params);
}

void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params)
{
    if (glEntry15.glGetQueryObjectiv)
        glEntry15.glGetQueryObjectiv(id, pname, params);
}

void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params)
{
    if (glEntry15.glGetQueryObjectuiv)
        glEntry15.glGetQueryObjectuiv(id, pname, params);
}

void APIENTRY glBindBuffer (GLenum target, GLuint buffer)
{
    if (glEntry15.glBindBuffer)
        glEntry15.glBindBuffer(target, buffer);
}

void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers)
{
    if (glEntry15.glDeleteBuffers)
        glEntry15.glDeleteBuffers(n, buffers);
}

void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers)
{
    if (glEntry15.glGenBuffers)
        glEntry15.glGenBuffers(n, buffers);
}

GLboolean APIENTRY glIsBuffer (GLuint buffer)
{
    if (glEntry15.glIsBuffer)
        return glEntry15.glIsBuffer(buffer);
    return 0;
}

void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
{
    if (glEntry15.glBufferData)
        glEntry15.glBufferData(target, size, data, usage);
}

void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)
{
    if (glEntry15.glBufferSubData)
        glEntry15.glBufferSubData(target, offset, size, data);
}

void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data)
{
    if (glEntry15.glGetBufferSubData)
        glEntry15.glGetBufferSubData(target, offset, size, data);
}

GLvoid* APIENTRY glMapBuffer (GLenum target, GLenum access)
{
    if (glEntry15.glMapBuffer)
        return glEntry15.glMapBuffer(target, access);
    return 0;
}

GLboolean APIENTRY glUnmapBuffer (GLenum target)
{
    if (glEntry15.glUnmapBuffer)
        return glEntry15.glUnmapBuffer(target);
    return 0;
}

void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params)
{
    if (glEntry15.glGetBufferParameteriv)
        glEntry15.glGetBufferParameteriv(target, pname, params);
}

void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params)
{
    if (glEntry15.glGetBufferPointerv)
        glEntry15.glGetBufferPointerv(target, pname, params);
}

//
// vertex array object
//
GLboolean glextInitVertexArrayObjectEntry()
{
    static GLboolean ret = GL_FALSE;

    if (ret)
        return ret;
    
    do 
    {
        GET_FUN_ADDR(PFNGLBINDVERTEXARRAYPROC, glvaoEntry, glBindVertexArray);
        GET_FUN_ADDR(PFNGLDELETEVERTEXARRAYSPROC, glvaoEntry, glDeleteVertexArrays);
        GET_FUN_ADDR(PFNGLGENVERTEXARRAYSPROC, glvaoEntry, glGenVertexArrays);
        GET_FUN_ADDR(PFNGLISVERTEXARRAYPROC, glvaoEntry, glIsVertexArray);
        ret = GL_TRUE;
    } while (GL_FALSE);

    return ret;
}

void APIENTRY glBindVertexArray (GLuint array)
{
    if (glvaoEntry.glBindVertexArray)
        glvaoEntry.glBindVertexArray(array);
}

void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays)
{
    if (glvaoEntry.glDeleteVertexArrays)
        glvaoEntry.glDeleteVertexArrays(n, arrays);
}

void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays)
{
    if (glvaoEntry.glGenVertexArrays)
        glvaoEntry.glGenVertexArrays(n, arrays);
}

GLboolean APIENTRY glIsVertexArray (GLuint array)
{
    if (glvaoEntry.glIsVertexArray)
        return glvaoEntry.glIsVertexArray(array);
    return 0;
}